package com.qian.service.ddj.serviceImpl;

import com.qian.api.ddj.model.Cash;
import com.qian.api.ddj.model.CashDict;
import com.qian.api.ddj.model.CashQueryModel;
import com.qian.api.ddj.model.StatusDict;
import com.qian.api.ddj.service.ICashService;
import com.qian.api.ddj.util.YunbaUtils;
import com.qian.service.ddj.dao.BusinessCardRepository;
import com.qian.service.ddj.dao.CashRepository;
import com.qian.service.ddj.dao.UserRepository;
import com.qian.service.ddj.entity.*;
import com.yaowk.api.common.BaseException;
import com.yaowk.api.common.PageReq;
import com.yaowk.api.common.PageResp;
import com.yaowk.service.common.BaseRepository;
import com.yaowk.service.common.BaseServiceImpl;
import com.yaowk.service.system.dao.MenuRepository;
import com.yaowk.service.system.entity.SysMenu;
import com.yaowk.service.system.entity.SysRole;
import com.yaowk.service.system.entity.SysUser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import javax.persistence.criteria.*;
import javax.transaction.Transactional;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

@Slf4j
@Service
public class CashServiceImpl extends BaseServiceImpl<Cash, DdjCash, Integer> implements ICashService {

    @Autowired
    private CashRepository dao;
    @Autowired
    private UserRepository userDao;
    @Autowired
    private BusinessCardRepository businessCardDao;
    @Autowired
    private MenuRepository menuDao;
    @Autowired
    private YunbaUtils yunbaUtils;

    @Override
    protected BaseRepository<DdjCash, Integer> getDao() {
        return dao;
    }

    @Override
    protected DdjCash model2Entity(Cash model) {
        if (ObjectUtils.isEmpty(model)) {
            return null;
        }
        DdjCash cash = new DdjCash();
        cash.setUserId(model.getUserId());
        cash.setMoney(model.getMoney());
        cash.setCardPhone(model.getCardPhone());
        cash.setCardPerson(model.getCardPerson());
        cash.setCardNo(model.getCardNo());
        cash.setCardBank(model.getCardBank());
        cash.setBusinessCardId(model.getBusinessCardId());
        cash.setCreateTime(model.getCreateTime());
        cash.setFee(model.getFee());
        cash.setFinishTime(model.getFinishTime());
        cash.setStatus(model.getStatus());
        cash.setId(model.getId());
        cash.setTotal(model.getTotal());
        if (isNotEmpty(model.getUserId())) {
            DdjUser user = new DdjUser();
            user.setId(model.getUserId());
            cash.setUser(user);
        }
        cash.setType(model.getType());
        cash.setRemark(model.getRemark());
        return cash;
    }

    @Override
    protected Cash entity2model(DdjCash entity) {
        if (ObjectUtils.isEmpty(entity)) {
            return null;
        }
        Cash cash = new Cash();
        cash.setUserId(entity.getUserId());
        cash.setMoney(entity.getMoney());
        cash.setCardPhone(entity.getCardPhone());
        cash.setCardPerson(entity.getCardPerson());
        cash.setCardNo(entity.getCardNo());
        cash.setCardBank(entity.getCardBank());
        cash.setBusinessCardId(entity.getBusinessCardId());
        cash.setCreateTime(entity.getCreateTime());
        cash.setFee(entity.getFee());
        cash.setFinishTime(entity.getFinishTime());
        cash.setStatus(entity.getStatus());
        cash.setId(entity.getId());
        cash.setTotal(entity.getTotal());
        cash.setUsername(entity.getUser().getUsername());
        cash.setType(entity.getType());
        cash.setRemark(entity.getRemark());
        return cash;
    }

    public PageResp<Cash> findAll(final CashQueryModel model, PageReq pageReq) {
        PageRequest pageRequest = new PageRequest(pageReq.getPageNumber() - 1, pageReq.getPageSize(), new Sort(Sort.Direction.DESC, "createTime"));
        Page<DdjCash> page = dao.findAll(new Specification<DdjCash>() {
            @Override
            public Predicate toPredicate(Root<DdjCash> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                Path<Date> timePath = root.get("createTime");
                Path<Byte> statusPath = root.get("status");
                Predicate condition = criteriaBuilder.isNotNull(root.<Integer>get("id"));
                // 用户比较
                if (isNotEmpty(model.getUserId()))
                    condition = criteriaBuilder.and(condition, criteriaBuilder.equal(root.<Integer>get("userId"), model.getUserId()));
                // 时间比较
                if (isNotEmpty(model.getStatus()))
                    condition = criteriaBuilder.and(condition, criteriaBuilder.equal(statusPath, model.getStatus()));
                // 日期比较
                if (isNotEmpty(model.getBeginTime()) && isNotEmpty(model.getEndTime()))
                    condition = criteriaBuilder.and(condition, criteriaBuilder.between(timePath, model.getBeginTime(), model.getEndTime()));
                else if (isNotEmpty(model.getBeginTime()))
                    condition = criteriaBuilder.and(condition, criteriaBuilder.greaterThanOrEqualTo(timePath, model.getBeginTime()));
                else if (isNotEmpty(model.getEndTime()))
                    condition = criteriaBuilder.and(condition, criteriaBuilder.lessThanOrEqualTo(timePath, model.getEndTime()));
                return condition;

            }
        }, pageRequest);
        return page2pageResp(page);
    }

    @Override
    @Transactional
    public void cash(Cash cash) {
        DdjUser ddjUser = userDao.findOne(cash.getUserId());
        DdjBusinessGroup ddjBusinessGroup = null;

        // 先验证用户余额
        if (ddjUser.getBalance().compareTo(cash.getTotal()) < 0) {
            throw new BaseException("余额不足，无法提现");
        }

        // 用户余额提现
        if (cash.getType().equals(CashDict.USER_BALANCE)) {

            if (!CollectionUtils.isEmpty(ddjUser.getBusinesses())) {
                for (DdjBusiness business : ddjUser.getBusinesses()) {
                    ddjBusinessGroup = business.getBusinessGroup();
                    break;
                }
            }
            cash.setCardNo(ddjUser.getCardNo());
            cash.setCardPerson(ddjUser.getCardPerson());
            cash.setCardPhone(ddjUser.getCardPhone());
            cash.setCardBank(ddjUser.getCardBank());
        } else if (cash.getType().equals(CashDict.CARD_BALANCE)) {
            // 商户银行卡余额提现
            DdjBusinessCard ddjBusinessCard = businessCardDao.findOne(cash.getBusinessCardId());
            if (ddjBusinessCard.getBalance().compareTo(cash.getTotal()) < 0) {
                throw new BaseException("余额不足，无法提现");
            }
            ddjBusinessGroup = ddjBusinessCard.getBusinessGroup();

            cash.setCardNo(ddjBusinessCard.getCardNo());
            cash.setCardPerson(ddjBusinessCard.getCardPerson());
            cash.setCardPhone(ddjBusinessCard.getCardPhone());
            cash.setCardBank(ddjBusinessCard.getCardBank());

            int result = businessCardDao.decreaseBalance(cash.getBusinessCardId(), cash.getTotal());
            if (result != 1) {
                throw new BaseException("扣银行卡余额出错");
            }

        }


        if (isNotEmpty(ddjBusinessGroup)) {
            // 计算手续费
            BigDecimal fee = ddjBusinessGroup.getCashPercent().divide(new BigDecimal(100)).multiply(cash.getTotal()).add(ddjBusinessGroup.getCashFixed());
            if (fee.compareTo(ddjBusinessGroup.getMinCashFee()) < 0) {
                fee = ddjBusinessGroup.getMinCashFee();
            }
            BigDecimal money = cash.getTotal().subtract(fee);
            cash.setFee(fee);
            cash.setMoney(money);
        } else {
            throw new BaseException("没有商户");
        }

        if (ddjBusinessGroup.getMinCashMoney().compareTo(cash.getTotal()) > 0) {
            throw new BaseException("提现金额低于最低提现额");
        }

        dao.save(model2Entity(cash));

        int result = userDao.decreaseBalance(cash.getUserId(), cash.getTotal());
        if (result != 1) {
            throw new BaseException("扣余额出错");
        }

        try {
            List<String> userIds = new ArrayList<>();

            SysMenu sysMenu = new SysMenu();
            sysMenu.setUrl("/cashNotify");
            Byte type = 3;
            sysMenu.setType(type);
            sysMenu = menuDao.findOne(Example.of(sysMenu));
            if (!isNotEmpty(sysMenu)) {
                return;
            }
            Set<SysRole> sysRoles = sysMenu.getRoles();
            for (SysRole sysRole : sysRoles) {
                Set<SysUser> sysUsers = sysRole.getUsers();
                for (SysUser sysUser : sysUsers) {
                    yunbaUtils.publishAlias("收到新的提现申请", sysUser.getUsername());
                }
            }
        } catch (Exception e) {
            log.info(e.getMessage());
        }
    }

    @Override
    @Transactional
    public Cash save(Cash model) {
        if (model.isNew()) {
            throw new BaseException("id is null");
        }
        DdjCash ddjCash = dao.findOne(model.getId());
        ddjCash.setStatus(model.getStatus());
        ddjCash.setFinishTime(new Date());
        ddjCash.setRemark(model.getRemark());
        model = entity2model(dao.save(ddjCash));
        if (ddjCash.getStatus().equals(StatusDict.CASH_FAIL)) {
            int result = userDao.increaseBalance(ddjCash.getUserId(), ddjCash.getTotal());
            if (result != 1) {
                throw new BaseException("返还用户余额出错");
            }
            if (ddjCash.getType().equals(CashDict.CARD_BALANCE)) {
                result = businessCardDao.increaseBalance(ddjCash.getBusinessCardId(), ddjCash.getTotal());
                if (result != 1) {
                    throw new BaseException("返还银行卡余额出错");
                }
            }
        }
        return model;
    }
}
